Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit fdbe84e87797ec90bc469f8d3ec5358261e9b102


Parents : 2eff200
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-05-02T22:49:21-05:00

fix(App.vue): implement polling guard to prevent overlapping updates and ensure proper state management

Changes

2 files changed, 39 insertions(+), 25 deletions(-)


Diff

diff --git a/meshchatx/src/frontend/components/App.vue b/meshchatx/src/frontend/components/App.vue
index be5c9c9c..e0a07cad 100644
--- a/meshchatx/src/frontend/components/App.vue
+++ b/meshchatx/src/frontend/components/App.vue
@@ -811,6 +811,8 @@ export default {
clearInterval(this._propagationSyncPollTimer);
this._propagationSyncPollTimer = null;
}
+ // Clear polling guard flag on unmount
+ this._isPropagationSyncPolling = false;
this.stopShell();
this.clearWsShellUiTimers();
if (this.endedTimeout) clearTimeout(this.endedTimeout);
@@ -1498,32 +1500,41 @@ export default {
await this.updatePropagationNodeStatus();
+ // Guard to prevent overlapping poll calls
+ this._isPropagationSyncPolling = false;
+
const poll = async () => {
- await this.updatePropagationNodeStatus();
- if (this.isSyncingPropagationNode) {
- ToastUtils.loading(this.propagationSyncLiveToastMessage(), 0, propagationSyncToastKey);
- return;
- }
- if (this._propagationSyncPollTimer != null) {
- clearInterval(this._propagationSyncPollTimer);
- this._propagationSyncPollTimer = null;
- }
- ToastUtils.dismiss(propagationSyncToastKey);
- const status = this.propagationNodeStatus?.state;
- const messagesReceived = this.propagationNodeStatus?.messages_received ?? 0;
- const messagesStored = this.propagationNodeStatus?.messages_stored ?? 0;
- const deliveryConfirmations = this.propagationNodeStatus?.delivery_confirmations ?? 0;
- const messagesHidden = this.propagationNodeStatus?.messages_hidden ?? 0;
- if (status === "complete" || status === "idle") {
- const base = this.$t("app.sync_complete", { count: messagesReceived });
- const details = `${messagesStored} stored, ${deliveryConfirmations} confirmations, ${messagesHidden} hidden`;
- ToastUtils.success(`${base} (${details})`);
- } else {
- ToastUtils.error(
- this.$t("app.sync_error", {
- status: this.propagationSyncStatusLabel(status),
- })
- );
+ if (this._isPropagationSyncPolling) return;
+ this._isPropagationSyncPolling = true;
+ try {
+ await this.updatePropagationNodeStatus();
+ if (this.isSyncingPropagationNode) {
+ ToastUtils.loading(this.propagationSyncLiveToastMessage(), 0, propagationSyncToastKey);
+ return;
+ }
+ if (this._propagationSyncPollTimer != null) {
+ clearInterval(this._propagationSyncPollTimer);
+ this._propagationSyncPollTimer = null;
+ }
+ ToastUtils.dismiss(propagationSyncToastKey);
+ const status = this.propagationNodeStatus?.state;
+ const messagesReceived = this.propagationNodeStatus?.messages_received ?? 0;
+ const messagesStored = this.propagationNodeStatus?.messages_stored ?? 0;
+ const deliveryConfirmations = this.propagationNodeStatus?.delivery_confirmations ?? 0;
+ const messagesHidden = this.propagationNodeStatus?.messages_hidden ?? 0;
+ if (status === "complete" || status === "idle") {
+ const base = this.$t("app.sync_complete", { count: messagesReceived });
+ const details = `${messagesStored} stored, ${deliveryConfirmations} confirmations, ${messagesHidden} hidden`;
+ ToastUtils.success(`${base} (${details})`);
+ } else {
+ ToastUtils.error(
+ this.$t("app.sync_error", {
+ status: this.propagationSyncStatusLabel(status),
+ })
+ );
+ }
+ } finally {
+ this._isPropagationSyncPolling = false;
}
};
@@ -1560,6 +1571,8 @@ export default {
clearInterval(this._propagationSyncPollTimer);
this._propagationSyncPollTimer = null;
}
+ // Clear the polling guard flag
+ this._isPropagationSyncPolling = false;
ToastUtils.dismiss(propagationSyncToastKey);
await this.updatePropagationNodeStatus();
},

diff --git a/tests/frontend/AppPropagationSync.test.js b/tests/frontend/AppPropagationSync.test.js
index 4e9b993f..2030b3ab 100644
--- a/tests/frontend/AppPropagationSync.test.js
+++ b/tests/frontend/AppPropagationSync.test.js
@@ -27,6 +27,7 @@ function makeSyncContext(axiosMock, tOverrides = {}) {
},
propagationNodeStatus: null,
_propagationSyncPollTimer: null,
+ _isPropagationSyncPolling: false,
propagationSyncLiveToastMessage: App.methods.propagationSyncLiveToastMessage,
propagationSyncStatusLabel: App.methods.propagationSyncStatusLabel,
get isSyncingPropagationNode() {


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────